// SampleClient.cpp
//
// by Brian Schick
// 5/4/2010
//
// For Week 2, GSP460
// TCP networking - A sending client
//	using Winsock2.2
//
// Steps for a TCP client:
//	Creating a Socket for the Client 
//	Connecting to a Socket 
//	Sending and Receiving Data on the Client 
//	Disconnecting the Client 
//
// Notes:
// TODO:
//		To assure that all data is sent and received on a connected socket before it is closed, an application should use shutdown to close connection before calling closesocket. For example, to initiate a graceful disconnect:
//		Call WSAAsyncSelect to register for FD_CLOSE notification. 
//		Call shutdown with how=SD_SEND. 
//		When FD_CLOSE received, call recv until zero returned, or SOCKET_ERROR. 
//		Call closesocket. 
//	Note  The shutdown function does not block regardless of the SO_LINGER setting on the socket.
//

struct outputData
{
	int outputOne;
	int outputTwo;
	int outputThree;
};

#pragma once

#include "WSAErrors.h"
#include <iostream>
#include <iomanip>
#include <Winsock2.h>

#define MSG_TO_SEND "Client says 'Hello!'"
#define SRVR_ADDRESS "127.0.0.1"
#define SRVR_PORT 7654
#define BUFFER_SIZE 512

using namespace std;

void CleanWSA();
void ShutAndCleanSocket( SOCKET s );
int FireConnection();

int main( int argc, char * argv[] )
{
	int errcode = 0;
	//for( int i = 0; i < 100; i++ )
		errcode += FireConnection();
	return errcode;
}

int FireConnection()
{
	cout << "Hello World!" << endl;

	//0.  Initialize Winsock
	//	WSAData - a structure holding Winsock info.
	WSAData myWSAInfo;
	memset(&myWSAInfo, 0, sizeof(WSAData) ); // Initialize the struct to all 0s.
	long errcode = 0;
	// 0x0202: Ask for Winsock2.2: minor version 2 (high byte) of major version 2 (low byte)
	//	It doesn't seem to matter which version I ask for.  I always get ver 2.2?
	//	It does matter if it gets a good WSAData pointer.
	errcode = WSAStartup( 0x0202, &myWSAInfo ); 
	WSAErrorsToConsole::ShowWSAStartupError( errcode );
	// Lets see what I get
	cout << "desc: " << myWSAInfo.szDescription << endl; // Always shows Winsock ver 2.0.  Why?
	cout << "desc: " << myWSAInfo.szSystemStatus << endl;
	cout << setbase(16) << showbase << myWSAInfo.wHighVersion << setbase(10) << endl; // It IS 2.2!
	// Enough!  Did it work?
	if ( 0 != errcode )
	{
		errcode = WSACleanup();
		if ( SOCKET_ERROR == errcode )
		{
			cout << "WSAStartup: " << endl;
			WSAErrorsToConsole::ShowWSACleanupError( WSAGetLastError() );
		}
		return 1;
	}
	cout << "Winsock2 started..." << endl;


	//1.  Create a socket for the client
	//		Build socket
	SOCKET myServerSocket = INVALID_SOCKET;
	myServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP );
	if ( INVALID_SOCKET == myServerSocket )
	{
		cout << "socket " << endl;
		WSAErrorsToConsole::ShowSocketError( WSAGetLastError() );
		ShutAndCleanSocket( myServerSocket );
		CleanWSA();
		return 1;
	}

	//2.  Connect to a socket
	//	Need an address to connect to
    sockaddr_in srvrAddr; /* Local address */
    memset(&srvrAddr, 0, sizeof(srvrAddr));   /* Zero out structure */
    srvrAddr.sin_family = AF_INET;                /* Internet address family */
    srvrAddr.sin_addr.s_addr = inet_addr(SRVR_ADDRESS); /* Any incoming interface */
    srvrAddr.sin_port = htons(SRVR_PORT);      /* Dest port */
	//	Try to connect
	//	TODO - try to connect to a non-blocking socket

	errcode = connect( myServerSocket,
					(SOCKADDR*)&srvrAddr,
					sizeof(srvrAddr) );
	if ( SOCKET_ERROR == errcode )
	{
		cout << "connect " << endl;
		WSAErrorsToConsole::ShowConnectError( WSAGetLastError() );
		ShutAndCleanSocket( myServerSocket );
		CleanWSA();
		return 1;
	}

	// If iMode!=0, non-blocking mode is enabled.
	u_long iMode=1;
	int err = ioctlsocket(myServerSocket,FIONBIO,&iMode);

	while(1)
	{

		//3.  Send and Receive on the socket
		//		Need something to send
		char buff[BUFFER_SIZE];
		char buff2[BUFFER_SIZE];

		cin.getline(buff2, BUFFER_SIZE, '\n');

		if (buff2[0] == 'q')
			break;

		//strcpy( buff, MSG_TO_SEND);

		outputData od;
		od.outputOne = 7;
		od.outputTwo = 8;
		od.outputThree = 9;

		//errcode = send( myServerSocket, buff, strlen(buff)+1, 0);
		errcode = send( myServerSocket, (char*)&od, sizeof(od), 0);
		
		if ( SOCKET_ERROR == errcode )
		{
			cout << "send " << endl;
			WSAErrorsToConsole::ShowSendError( WSAGetLastError() );
			ShutAndCleanSocket( myServerSocket );
			CleanWSA();
			return 1;
		}
	}

	//4.  Disconnect the client
	//	Part A.) Close socket for sending...
	//		Here, should retry WSAEINPROGRESS errors until success.
	errcode = shutdown( myServerSocket, SD_SEND );
	if ( SOCKET_ERROR == errcode )
	{
		cout << "shutdown " << endl;
		WSAErrorsToConsole::ShowShutdownError( WSAGetLastError() );
		ShutAndCleanSocket( myServerSocket );
		CleanWSA();
		return 1;
	}
	//	Part B.) Wait for server to close connection
	do
	{
		char buff[BUFFER_SIZE];
		errcode = recv( myServerSocket, buff, BUFFER_SIZE, 0);
		if ( errcode > 0 )
			cout << "Received " << errcode << " bytes." << endl;
		else if ( errcode == 0 )
			cout << "Closed." << endl;
		else
		{
			cout << "recv " << endl;
			WSAErrorsToConsole::ShowRecvError( WSAGetLastError() );
			ShutAndCleanSocket( myServerSocket );
			CleanWSA();
			return 1;
		}
	}
	while( errcode > 0 );

	ShutAndCleanSocket( myServerSocket );

	//5.  Release Winsock
	CleanWSA();

	cout << "SampleClient is done." << endl;
	return 0;
}

void ShutAndCleanSocket( SOCKET s )
{
	long errcode = 0;
	errcode = shutdown( s, SD_BOTH );
	if ( SOCKET_ERROR == errcode )
	{
		WSAErrorsToConsole::ShowShutdownError( errcode );
	}

	errcode = closesocket( s );
	if ( SOCKET_ERROR == errcode )
	{
		WSAErrorsToConsole::ShowCloseSocketError( errcode );
	}
}

void CleanWSA()
{
	long errcode = 0;

	errcode = WSACleanup();
	if ( errcode == SOCKET_ERROR )
	{
		WSAErrorsToConsole::ShowWSACleanupError( WSAGetLastError() );
	}
}

